package dk.aiszone.lang.messer;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;

import dk.aiszone.lang.autosource.generatorobjects.AbstractBody;
import dk.aiszone.lang.autosource.generatorobjects.ClassBody;
import dk.aiszone.lang.autosource.generatorobjects.JavaFile;
import dk.aiszone.lang.autosource.generatorobjects.Method;
import dk.aiszone.lang.autosource.generatorobjects.interfaces.ChildContainer;
import dk.aiszone.lang.messer.autosource.interfaces.ModifiableObject;

/**
 * <code>Iterator</code> implementation used for traversing the modifications of a <code>JavaFile</code>
 * 
 * @author Ais
 */
public class FileMesser implements Iterator<JavaFile> {

    private final JavaFile baseFile;
    private List<Modification> modification;
    private Iterator<Modification> iterator;

    /**
     * Constructor for the <code>FileMesser</code>.
     * 
     * @param baseFile
     *            The <code>JavaFile</code> to modify.
     */
    public FileMesser(JavaFile baseFile) {
        this.baseFile = baseFile;

        modification = extractModifiableObjects(baseFile);
        iterator = modification.iterator();
    }

    @Override
    public boolean hasNext() {
        return iterator.hasNext();
    }

    @Override
    public JavaFile next() {
        Modification modification = iterator.next();
        if (modification.modificationIndex == -1) {
            modification.modifiable.revert();
            modification = iterator.next();
        }

        modification.modifiable.modify(modification.modificationIndex);

        return baseFile;
    }

    @Override
    public void remove() {
        throw new UnsupportedOperationException("Remove not supported");
    }

    private List<Modification> extractModifiableObjects(JavaFile baseFile) {
        List<Modification> result = new ArrayList<Modification>();

        AbstractBody<?> abstractBody = baseFile.getMainClass();
        if (abstractBody instanceof ClassBody) {
            ClassBody classBody = (ClassBody)abstractBody;
            for (Iterator<Method> iterator = classBody.getMethods().iterator(); iterator.hasNext();) {
                Method method = iterator.next();
                if (method instanceof ChildContainer) {
                    result.addAll(handleModifibleObject((ChildContainer)method));
                }
            }
        }

        return result;
    }

    private Collection<? extends Modification> handleModifibleObject(ChildContainer childContainer) {
        List<Modification> result = new ArrayList<Modification>();
        if (childContainer instanceof ModifiableObject) {
            ModifiableObject modifiableObject = (ModifiableObject)childContainer;
            if (modifiableObject.isModifiable()) {
                for (int index = 0; index < modifiableObject.getNumberOfSupportedModifications(); index++) {
                    result.add(new Modification(modifiableObject, index));
                }
                result.add(new Modification(modifiableObject));
            }
        }

        for (int i = 0; i < childContainer.getChildCount(); i++) {
            result.addAll(handleModifibleObject(childContainer.getChild(i)));
        }
        return result;
    }

    private class Modification {

        /* pp */ModifiableObject modifiable;
        /* pp */int modificationIndex;

        /* pp */Modification(ModifiableObject modifiable) {
            this.modifiable = modifiable;
            this.modificationIndex = -1;
        }

        /* pp */Modification(ModifiableObject modifiable, int modificationIndex) {
            this.modifiable = modifiable;
            this.modificationIndex = modificationIndex;
        }
    }
}